#!/usr/bin/python
# -*- coding: utf-8 -*-

'''
Created on 18/03/2010

@author: sebas
'''
import logging
import time
import os
import re
from gdata.service import Query
from gdata.books.service import BookService

from model import Book
from model import Author

import uno

from unohelper import Base, systemPathToFileUrl, absolutize
from os import getcwd
from com.sun.star.beans import PropertyValue
from com.sun.star.uno import Exception as UnoException
from com.sun.star.io import IOException, XOutputStream
import sys

class Unoconv(object):
  '''
    Hecha en base a un ejemplo extraido de http://udk.openoffice.org/python/samples/ooextract.py
  '''
  class OutputStream(Base, XOutputStream):
    def __init__(self):
        self.closed = 0
    def closeOutput(self):
        self.closed = 1
    def writeBytes(self, seq):
        sys.stdout.write(seq.value)
    def flush(self):
        pass

  FILTERS = {
    "html": "HTML (StarWriter)",
    "pdf": "writer_pdf_Export"
  }

  def __init__(self, logger):
    self.url = "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext"
    self.logger = logger
    self.resolver = Resolver()
    self.desktop = None

  def __desktop(self):
    if self.desktop is None:
      ctxLocal = uno.getComponentContext()
      ctx = ctxLocal.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", ctxLocal).resolve(self.url)
      self.desktop = ctx.ServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
    return self.desktop    

  def convert(self, path, target):
    format = self.resolver.extension(target)
    
    if format not in self.FILTERS.keys():
      raise "Formato '%s' no soportado" % format

    self.logger.info ("Convirtiendo %s a %s" % (path, target))

    cwd = systemPathToFileUrl(getcwd())
    inProps = PropertyValue("Hidden" , 0 , True, 0),

    outProps = (
      PropertyValue("FilterName" , 0, self.FILTERS[format] , 0),
      PropertyValue("Overwrite" , 0, True , 0),
      PropertyValue("OutputStream", 0, Unoconv.OutputStream(), 0)
    )

    doc = None    
    try:
      fileUrl = absolutize(cwd, systemPathToFileUrl(path))
      doc = self.__desktop().loadComponentFromURL(fileUrl , "_blank", 0, inProps)

      if not doc:
        raise UnoException("Couldn't open stream for unknown reason", None)

      destUrl = absolutize(cwd, systemPathToFileUrl(target))
      doc.storeToURL(destUrl, outProps)
    except IOException:
      self.logger.exception("Error convirtiendo %s a %s" % (path, target))
      return None
    except UnoException:
      self.logger.exception("Error convirtiendo %s a %s" % (path, target))
      return None
    finally:
      if doc:
        doc.dispose()
    
    return target

class EBookConvert (object):
  def convert(self, source, target, opts):
    import init_calibre
    from calibre.ebooks.conversion.cli import main

    args = list()
    args.append("dummy")
    args.append (source)
    args.append (target)
    
    args.extend(opts)
    
    main (map(lambda x: x.decode("utf-8"), args))

class Resolver(object):
  DELAY = 1

  ALLOWED_EXTENSIONS = ["DOC", "LIT", "PDF", "RTF", "TXT", "HTML", "ODT", "EPUB"]

  PROGRAMS = dict(
    doc = ["ooffice"],
    pdf = ["kpdf", "evince"],
    txt = ["kate", "gedit"],
    html = ["firefox"]
  )
    
  replaces = {
   'á': 'a',
   'Á': 'a',
   'é': 'e',
   'É': 'e',
   'í': 'i',
   'Í': 'i',
   'ó': 'o',
   'Ó': 'o',
   'ú': 'u',
   'Ú': 'u',
   'ñ': 'n',
   'Ñ': 'n',
   ',': '',
   '(': '',
   ')': '',
   '-': '',
   '/': ''
  }

  patterns = [re.compile("(?P<author>[^-]+) - (?P<title>[^_]+)")]
  
  queries = [
    lambda t, a:'"inauthor:%s" "intitle:%s"' % (a, t),
    lambda t, a:'"%s" "%s"' % (a, t),
    lambda t, a:'"%s"' % (t)    
  ]  

  def __init__(self, logger=None):
    self.bs = BookService()
    if logger is None:
      self.logger = logging.getLogger("resolver")
    else:
      self.logger = logger
 
  def open(self, file):
    ext = self.extension(file)
    if ext not in self.PROGRAMS:
      print "El formato %s no está soportado" % ext
      return
    
    for p in self.PROGRAMS[ext]:
      if os.system("which %s" % p) == 0:
        print ' ejecutando %s "%s" &' % (p, file)
        os.system('%s "%s" &' % (p, file))
        return
    
    print "No se encontró un programa para manejar archivos de tipo %s, alternativas = %s" % (ext, self.PROGRAMS[ext])
  
  def epub(self, item, target):
    bf = item.bookfile
    book = bf.book
    author = book.authors[0].sortname
    
    name = "%s.epub" % book.title
    base = os.path.join(target, author[0], author)
    if not os.path.exists(base):
      os.makedirs(base)

    opts = list()
    opts.append('--output-profile=nook')
    opts.append('--title=%s' % book.title)
    opts.append('--authors=%s' % (" & ".join(map(lambda x : x.name, book.authors))))
    opts.append('--author-sort=%s' % author)
    opts.append('--language=es')
    
    return os.path.join(base, name), opts
  
  def valid_format(self, file):
    return self.extension(file).upper() in self.ALLOWED_EXTENSIONS
     
  def extension(self, file):
    return os.path.splitext(file)[1][1:] if file.find(".") != -1 else None
    
  def change_extension(self, location, extension):
    return "%s.%s" % (os.path.splitext(location)[0], extension) 
    
  def infer_data(self, entry):
    match = self.__match_book(os.path.split(entry)[1])
    if match and match.group("author"):
      if os.path.isfile(entry):
        return (os.path.splitext(match.group("title"))[0], match.group("author"))
    return (None, None)

  def book(self, title, author, gbook):
      return Book(gbook.title.text if gbook is not None else title, self.authors(author, gbook), gbook)

  def authors(self, name, gbook):
    authors = list()
    if gbook is not None:
      names = gbook.to_dict()["authors"]
    else:
      names = [name]
    
    for n in names:
      authors.append(self.__author(n))

    return authors

  def name_from_sortname(self, sortname):
    if sortname.find(",") != -1:
      fullname = map(lambda x: x.strip(), sortname.split(","))
      fullname.reverse()
      return " ".join(fullname)
    return sortname
    
  def sortname_from_name(self, name):
    nn = name.rpartition(" ")
    return "%s, %s" % (nn[2], nn[0])
    
  def __author(self, name):
    if name.find(",") != -1:
      return Author(self.name_from_sortname(name), name)
    elif name.find(" ") != -1:
      return Author(name, self.sortname_from_name(name))
    return Author(name, name)

  def gbook(self, title, author):
    result = None
    gbook = None
    for q in self.queries:
      text = q(title, author)
      self.logger.debug("\tHaciendo consulta: %s" % text)
      q = Query(text_query=text, params={"lr": "es"})
      data = self.bs.search(q, max_results="100")
      if data.entry:
        result = self.__get_best_match(title, author, data.entry)
        if result:
          break
      time.sleep(self.DELAY)

    if result:
      gbook = self.bs.get_by_google_id(result.get_google_id())
      time.sleep(self.DELAY)
      self.logger.info ("\tSe encontro gbook = %s para '%s'" % (result.title.text, title))

    return gbook

  def __get_best_match(self, title, authors, candidates):
    max = -1
    max_authors = -1
    winner = None
    for b in candidates:
      dict = b.to_dict()
      if dict.has_key("authors") and dict["authors"] is not None:
        try:
          gauthors = " ".join(dict["authors"])
        except:
          logging.debug ("Error obteniendo autores: %s" % dict["authors"])
          gauthors = ""
      else:
        gauthors = ""
      ratio = self.__get_match_ratio(title, b.title.text)
      ratio_authors = self.__get_match_ratio(authors, gauthors)
      self.logger.debug("Ratios: titulo('%s' - '%s') = %s, autor('%s' - '%s') = %s" % (b.title.text, title, ratio, gauthors, authors, ratio_authors))
      if ratio > max and ratio_authors > max_authors:
        max = ratio
        max_authors = ratio_authors
        winner = b

    self.logger.debug("Info para %s = %s de matches en %s" % (title, max, (winner.title.text if winner else "None")))

    return winner if max > 0.7 and max_authors > 0.7 else None

  def __get_match_ratio(self, one, two):
    wone = self.__normalize(one).split(" ")
    wtwo = self.__normalize(two).split(" ")

    ratio = -1
    count = 0
    for w in wone:
      count += 1 if w in wtwo else 0

    if count > 0:
      ratio = float(count) / float(len(wone))
    
    self.logger.debug ("Ratio de %s al comparar %s con %s" % (ratio, self.__normalize(one), self.__normalize(two)))

    return ratio

  def __normalize(self, str):
    str = str.lower()
    for f, t in self.replaces.items():
      str = str.replace(f, t)
    return str

  def __match_book(self, candidate):
    for p in self.patterns:
      m = p.match(candidate)
      if m:
        return m
    return None
